---@diagnostic disable: lowercase-global

-- initial points that form 2024 string
initial_points = {
17,74,
40,32,
80,36,
90,85,
20,122,
19,138,
112,133,
140,132,
174,133,
168,40,
120,44,
112,128,
167,57,
192,36,
238,33,
248,73,
187,110,
182,143,
252,138,
304,140,
309,29,
250,96,
322,103
}

points = {} -- points, that represents dragon

buttons = {
    {txt = "speed multiplier limit on", 
    fn = function()
        limitedSpeed = not limitedSpeed
        if limitedSpeed then buttons[1].txt = "speed multiplier limit on" else buttons[1].txt = "speed multiplier limit off" end
        fruitSFX:play()
    end},

    {txt = "music on",
    fn = function()
        if music:getVolume() > 0 then
            music:setVolume(0)
            buttons[2].txt = "music off"
        else
            music:setVolume(1)
            buttons[2].txt = "music on"
        end
        fruitSFX:play()
    end},

    {txt = "sound effects on",
    fn = function()
        if fruitSFX:getVolume() > 0 then
            fruitSFX:setVolume(0)
            starSFX:setVolume(0)
            starDownSFX:setVolume(0)
            deathSFX:setVolume(0)
            buttons[3].txt = "sound effects off"
        else
            fruitSFX:setVolume(1)
            starSFX:setVolume(1)
            starDownSFX:setVolume(1)
            deathSFX:setVolume(1)
            buttons[3].txt = "sound effects on"
        end
        fruitSFX:play()
    end
        },
        {txt = "made by uldo", fn = function() love.system.openURL("http://uldo.su/") end}
}

lg = love.graphics

bkgDecor = {}
bkgImage = lg.newImage("decor.png")
bkgSpriteBatch = lg.newSpriteBatch(bkgImage)

Obstacle = require("obstacle")
Fruit = require("fruit")
Timer = require("timer")
Decor = require("decor")


obstacles = {}
fruits = {}

music = love.audio.newSource("music.ogg", "stream")
fruitSFX = love.audio.newSource("fruit.ogg", "static")
starSFX = love.audio.newSource("star.ogg", "static")
starDownSFX = love.audio.newSource("starDown.ogg", "static")
deathSFX = love.audio.newSource("death.ogg", "static")

-- helper functions
function table.find(array,value)
	 for i,v in ipairs(array) do
		 if v == value then
			 return i
		 end
	 end
	 return nil
end
function lerp(a,b,t) return (1-t)*a + t*b end
function distance(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end
function angle(x1,y1, x2,y2) return math.atan2(y2-y1, x2-x1) end

dragonSpeed = 25
dragonColors = {109/255, 140/255, 64/255}
dragonHeadAngle = 0
maxDist = 13
circled = false

activated = false
limitedSpeed = true

local bkgColor = {217/255, 222/255, 222/255}

-- Timers
timers = {
    fruitSpawn = Timer:new(5.0, 5.0, function() Fruit:new() end, {isMultiplied = true, isRepeat = true}),
    obstacleSpawn = Timer:new(7.0, 7.0, function() Obstacle:new() end, {isMultiplied = true, isRepeat = true}),

    invicibility = Timer:new(0.0, 7.0, function() starDownSFX:play() end),
    crunch = Timer:new(0.0, 15.0, function() starDownSFX:play() end),
    slowmo = Timer:new(0.0, 10.0, function()  starDownSFX:play() if slowed then slowed = false speedMultiplier = speedMultiplier * 1.5 end end),
    speedUp = Timer:new(5.0, 5.0, function() 
            if (limitedSpeed and speedMultiplier < 15) or not limitedSpeed then
                if slowed then
                    speedMultiplier = speedMultiplier + 0.1/1.5
                else
                    speedMultiplier = speedMultiplier + 0.1 
                end
                timers.speedUp.max = 5.0 * math.max(1,speedMultiplier/2)
                music:setPitch(1 + (speedMultiplier/10))
            else
                timers.speedUp.isRepeat = false
                timers.speedUp:stop()
            end
    end, {isRepeat = true}),

    bkgDecor = Timer:new(7.0, 7.0, function() 
        timers.bkgDecor.max = love.math.random(3.0, 8.0) 
        Decor:new()
    end, {isRepeat = true, isMultiplied = true}),

}

speedMultiplier = 1.0
bestMultiplier = 1.0
slowed = false

score = 0
highScore = 0

local cloudSprite = lg.newImage("clouds.png")
local cloudY = love.math.random(0,800)


function love.load()

    if love.system.getOS() == "Android" then
        love.window.setMode(lg.getWidth(), lg.getHeight(), {resizable = false, fullscreen = true})
    end

    if love.filesystem.getInfo("save.txt") then
        file = love.filesystem.read("save.txt")
        d = string.find(file,"/")
        highScore = tonumber(string.sub(file, 1, d - 1))
        bestMultiplier = tonumber(string.sub(file, d + 1, -1))
    end
	reset()
	lg.setLineJoin("none")
	lg.setLineWidth(8)
	lg.setLineStyle("smooth")

    lg.setFont(lg.newFont("lampfont.ttf"))

    handsSprite = lg.newImage("hands.png")
    headSprite = lg.newImage("head.png")
    tailSprite = lg.newImage("tail.png")

    music:setLooping(true)
end

function love.update(dt)

    if cloudY < 1200 then
        cloudY = cloudY + dt * speedMultiplier * 15
    else
        cloudY = 0
    end

	if not activated then return end

	local dist = 0
	for i,pos in ipairs(points) do
		if i > 2 then
			if i%2 ~= 0 then
				dist = distance(pos, points[i+1], points[i-2],points[i-1])
			end
			if math.abs(dist) > maxDist then
				points[i] = lerp(pos, points[i-2], dt * dragonSpeed)
			end
		end
	end

	if not timers.crunch.isStopped then
		timers.fruitSpawn:update(dt)
		timers.obstacleSpawn:update(dt)
	end

    for k,timer in pairs(timers) do
        timer:update(dt)
    end

	for i,fruit in ipairs(fruits) do
		fruit:update(dt)
	end

	for i,obstacle in ipairs(obstacles) do
		obstacle:update(dt)
	end

    for i,decor in ipairs(bkgDecor) do
        decor:update(dt)
    end
end


function reset()

	for i=#initial_points+1, #points do
		points[i] = null
	end

	for i=1, #initial_points do
		if i % 2 ~= 0 then
			points[i] = initial_points[i] + lg.getWidth()/2 - 170
		else
			points[i] = initial_points[i] + lg.getHeight()/2 - 100
		end
	end

	fruits = {}
	obstacles = {}

    for k, timer in pairs(timers) do
        timer:reset()
    end

    if highScore < score then
        highScore = score
    end
	score = 0

    if slowed then
        slowed = false
        speedMultiplier = speedMultiplier * 1.5
    end

    if bestMultiplier < speedMultiplier then
        bestMultiplier = speedMultiplier
    end
    speedMultiplier = 1.0


    dragonHeadAngle = math.pi - 1

    bkgDecor = {}

    for i=1, love.math.random(3,10) do
        local d = Decor:new()
        d.y = love.math.random(20, lg.getHeight() - 17 )
    end

    love.filesystem.write("save.txt", tostring(highScore).."/"..tostring(bestMultiplier))
    music:setPitch(1)

end

function love.draw()
    lg.clear(bkgColor)

    --draw bkg
    table.sort(bkgDecor, function(a,b)
            return a.y < b.y
    end)
    --bkgSpriteBatch:clear()
    for _, decor in ipairs(bkgDecor) do
        --bkgSpriteBatch:add(decor.q, decor.x, decor.y)
        lg.draw(bkgImage, decor.q, decor.x, decor.y)
    end

    --draw clouds

    for i=0, math.ceil(lg.getWidth()/800) - 1 do
        lg.draw(cloudSprite, 800 * i, cloudY)
        lg.draw(cloudSprite, 800 * i, cloudY - 1200)
    end

	-- drawing dragon
    local a = angle(points[#points-1],points[#points],points[#points-3],points[#points-2])
    --outline
    lg.setColor(0,0,0)
    lg.setLineWidth(12)
	for i=2, #points do
        if i % 2 == 0 then
            lg.circle("fill", points[i-1], points[i], 7)
        end
    end
    --body
	lg.line(points)
    lg.setColor(dragonColors)
    lg.setLineWidth(8)
	lg.line(points)
	for i=2, #points do
        if i % 2 == 0 then
            lg.circle("fill", points[i-1], points[i], 5)
        end
    end

	lg.setColor(1,1,1)
    if activated then 
        for i=2, #points do
            if i % 10 == 0 then
                a =  angle(points[i-1], points[i], points[i-3], points[i-2])
                lg.draw(handsSprite, points[i-1], points[i], a, 1,1, 14,14)
            end
        end
        lg.draw(headSprite, points[1], points[2], dragonHeadAngle, 1,1, 6, 14)
    end


	--drawing fruits
	for i, fruit in ipairs(fruits) do
		fruit:draw()
	end

	--drawing obstacles
	for i, obstacle in ipairs(obstacles) do
        obstacle:draw()
	end

	--draw timers
    local activeTimers = 0
    --activeTimers = timers.speedUp:draw(activeTimers, {1,1,1})
    activeTimers = timers.invicibility:draw(activeTimers, {52/255,160/255,56/255})
    activeTimers = timers.crunch:draw(activeTimers, {164/255,12/255,44/255})
    activeTimers = timers.slowmo:draw(activeTimers, {79/255,64/255,207/255})

    --print info
    lg.setColor(0.6,0.75,0.8, 0.7)
    if activated then
        local scoreString = "score: "..score
        local speedString = "speed: x"..string.format("%.1f", speedMultiplier)
        lg.rectangle("fill", 5, 5, math.max(#scoreString, #speedString) * 9.5 + 5, 40 )
        lg.setColor(0,0,0)
        lg.print(scoreString, 10, 10)
        lg.print(speedString, 10, 30)
    else
        local scoreString = "high score: "..highScore
        local speedString = "best speed: x"..string.format("%.1f", bestMultiplier)
        lg.rectangle("fill", 5, 5, math.max(#scoreString, #speedString) * 9.5 + 5, 40 )
        lg.setColor(0,0,0)
        lg.print(scoreString, 10, 10)
        lg.print(speedString, 10, 30)

        for i,button in ipairs(buttons) do
            lg.setColor(0.6,0.75,0.8, 0.7)
            lg.rectangle("fill", 5, lg.getHeight() - 30 * i - 10, 280 - i * 30, 25)
            lg.setColor(0,0,0)
            lg.print(button.txt, 10, lg.getHeight() - 30 * i - 5)
        end
    end
    lg.setColor(1,1,1)

end

function love.mousepressed( x, y, button, istouch )
	if button == 1 and not activated then
        if distance( x, y, points[1], points[2] ) < 200 then
		    activated = not activated
            music:play()
        end
        for i,button in ipairs(buttons) do
            if x > 10 and x < 280 and y > lg.getHeight() - 30 * i - 10 and y < lg.getHeight() - 30 * i + 15 then
                button.fn()
            end
        end
	end
end

function love.mousemoved( x, y, dx, dy, istouch)
	if distance(x,y, points[1],points[2]) > 50 or not activated then
		if not love.mouse.isVisible() then
			love.mouse.setVisible(true)
		end
		return
	end
	if love.mouse.isVisible() then
		love.mouse.setVisible(false)
	end
	points[1] = x
	points[2] = y
    dragonHeadAngle = angle(points[3],points[4], points[1], points[2])

	local headTailDist = distance(x,y,points[#points-1], points[#points])
	if headTailDist < 20 and not circled then
		local top, bottom = 600, 0
		local right, left = 0, 800
		for i, point in ipairs(points) do
			if i % 2 == 0 then
				if point < top then
					top = point
				elseif point > bottom then
					bottom = point
				end
			else
				if point > right then
					right = point
				elseif point < left then
					left = point
				end
			end
		end
		for i,f in ipairs(fruits) do
			if f.x > left and f.x < right and f.y > top and f.y < bottom then
				eatFruit(f)
			end
		end
		circled = true
	elseif headTailDist > 100 and circled then
		circled = false
	end
end

function eatFruit(fruit)
	local i = table.find(fruits,fruit)
	table.remove(fruits, i)
	score = score + 10
    if score %3 == 0 then
        table.insert(points, points[#points-1])
        table.insert(points, points[#points-1])
    end
	if fruit.shell then
		timers.invicibility:start()
	elseif fruit.crunch then
		timers.crunch:start()
    elseif fruit.slowmo then
        timers.slowmo:start()
        if not slowed then
            speedMultiplier = speedMultiplier / 1.5
            slowed = true
        end
	end

    if fruit.shell or fruit.crunch or fruit.slowmo then
        starSFX:play()
    else
        fruitSFX:play()
    end
end

